package com.fable.sp.core.util;

import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * .
 *
 * @author stormning on 16/5/23.
 */
public class HttpUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtil.class);

    private static final String INLINE_DISPOSITION_TYPE = "inline";

    private static final String ATTACHMENT_DISPOSITION_TYPE = "attachment";

    private static final String HEADER_ETAG = "ETag";

    private static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";

    private static final String HEADER_IF_NONE_MATCH = "If-None-Match";

    private static final String HEADER_LAST_MODIFIED = "Last-Modified";

    private static final String HEADER_X_REAL_IP = "X-Real-IP";

    private static final String HEADER_X_FORWARDED_FOR = "X-Forwarded-For";

    private static final String HEADER_USER_AGENT = "User-Agent";

    private static final String HEADER_CACHE_CONTROL = "Cache-Control";

    private static final String RESPONSE_CONTENT_DISPOSITION = "Content-Disposition";
    private static ThreadLocal<SimpleDateFormat> httpFormatter = new ThreadLocal<SimpleDateFormat>();

    private static ServletRequestAttributes getCurrentServletRequestAttributes() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        Assert.state(requestAttributes != null, "Could not find current request via RequestContextHolder");
        Assert.isInstanceOf(ServletRequestAttributes.class, requestAttributes);
        return (ServletRequestAttributes) requestAttributes;
    }

    public static HttpServletRequest getCurrentRequest() {
        return getCurrentServletRequestAttributes().getRequest();
    }

    public static HttpSession getCurrentSession() {
        return getCurrentRequest().getSession();
    }

    public static HttpServletResponse getCurrentResponse() {
        return getCurrentServletRequestAttributes().getResponse();
    }

    public static String getContextPath() {
        HttpServletRequest request = getCurrentRequest();
        String path = request.getContextPath();
        if (path == null) {
            return "";
        }
        int lastIndex = path.lastIndexOf("/");
        if (lastIndex > 0 && lastIndex == path.length() - 1) {
            return path.substring(lastIndex);
        }
        return path;
    }

    public static String getIP() {
        HttpServletRequest request = getCurrentRequest();
        String xForwardedFor = request.getHeader("$wsra");
        if (xForwardedFor != null) {
            return xForwardedFor;
        }
        xForwardedFor = request.getHeader(HEADER_X_REAL_IP);
        if (xForwardedFor != null) {
            return xForwardedFor;
        }
        xForwardedFor = request.getHeader(HEADER_X_FORWARDED_FOR);
        if (xForwardedFor != null) {
            int spaceIndex = xForwardedFor.indexOf(',');
            if (spaceIndex > 0) {
                return xForwardedFor.substring(0, spaceIndex);
            } else {
                return xForwardedFor;
            }
        }
        return request.getRemoteAddr();
    }

    public static String getUserAgent() {
        return StringUtils.defaultString(getCurrentRequest().getHeader(HEADER_USER_AGENT)).toLowerCase();
    }

    public static boolean isIOS() {
        return getUserAgent().contains("iphone");
    }

    public static boolean isAndroid() {
        return getUserAgent().contains("android");
    }

    public static void setCookie(String name, String value) {
        Cookie cookie = new Cookie(name, value);
        cookie.setMaxAge((int) (DateUtils.MILLIS_PER_DAY * 365 / DateUtils.MILLIS_PER_SECOND));
        getCurrentResponse().addCookie(cookie);
    }

    public static String getCookie(String name) {
        Cookie[] cookies = getCurrentRequest().getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (name.equals(cookie.getName())) {
                    return name;
                }
            }
        }
        return StringUtils.EMPTY;
    }

    public static SimpleDateFormat getHttpDateFormatter() {
        if (httpFormatter.get() == null) {
            httpFormatter.set(new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US));
            httpFormatter.get().setTimeZone(TimeZone.getTimeZone("GMT"));
        }
        return httpFormatter.get();
    }

    public static String getHeader(String name) {
        return getCurrentRequest().getHeader(name);
    }

    public static boolean hasHeader(String name) {
        return StringUtils.isNotBlank(getHeader(name));
    }

    public static boolean isModified(String etag, long last) {
        if (!(hasHeader(HEADER_IF_NONE_MATCH) && hasHeader(HEADER_IF_MODIFIED_SINCE))) {
            return true;
        } else {
            String browserEtag = getHeader(HEADER_IF_NONE_MATCH);
            if (!browserEtag.equals(etag)) {
                return true;
            } else {
                try {

                    Date browserDate = getHttpDateFormatter().parse(getHeader(HEADER_IF_MODIFIED_SINCE));
                    if (browserDate.getTime() >= last) {
                        return false;
                    }
                } catch (ParseException ex) {
                    LOGGER.error("Can't parse date", ex);
                }
                return true;
            }
        }
    }

    private static boolean canAsciiEncode(String string) {
        CharsetEncoder asciiEncoder = Charset.forName("US-ASCII").newEncoder();
        return asciiEncoder.canEncode(string);
    }

    public static void notFound() {
        notFound(null);
    }

    public static void ok() {
        getCurrentResponse().setStatus(HttpServletResponse.SC_OK);
    }

    public static void notFound(String error) {
        HttpServletResponse response = getCurrentResponse();
        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        if (StringUtils.isNotBlank(error)) {
            try {
                if (!response.isCommitted()) {
                    response.getWriter().write(error);
                }
            } catch (IOException e) {
                LOGGER.error("IO exception", e);
            }
        }
    }

    public static void notModified(String etag) {
        HttpServletResponse response = getCurrentResponse();
        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        if (etag != null) {
            response.setHeader(HEADER_ETAG, etag);
        }
    }

    public static void setContentTypeIfNotSet(String contentType) {
        HttpServletResponse response = getCurrentResponse();
        if (response.getContentType() == null) {
            response.setContentType(contentType);
        }
    }

}
